/*
 * Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package top.continew.admin.common.util.storage.platform;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import com.aliyun.auth.credentials.Credential;
import com.aliyun.auth.credentials.provider.StaticCredentialProvider;
import com.aliyun.oss.event.ProgressEvent;
import com.aliyun.oss.event.ProgressEventType;
import com.aliyun.oss.model.CannedAccessControlList;
import com.aliyun.oss.model.ObjectMetadata;
import com.aliyun.sdk.service.vod20170321.AsyncClient;
import com.aliyun.sdk.service.vod20170321.models.DeleteVideoRequest;
import com.aliyun.sdk.service.vod20170321.models.DeleteVideoResponse;
import com.aliyun.vod.upload.impl.UploadVideoImpl;
import com.aliyun.vod.upload.impl.VoDProgressListener;
import com.aliyun.vod.upload.req.UploadStreamRequest;
import com.aliyun.vod.upload.resp.UploadStreamResponse;
import darabonba.core.client.ClientOverrideConfiguration;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.dromara.x.file.storage.core.FileInfo;
import org.dromara.x.file.storage.core.InputStreamPlus;
import org.dromara.x.file.storage.core.ProgressListener;
import org.dromara.x.file.storage.core.UploadPretreatment;
import org.dromara.x.file.storage.core.exception.ExceptionFactory;
import org.dromara.x.file.storage.core.platform.FileStorage;

import java.io.InputStream;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Consumer;

/**
 * 阿里云VOD存储
 */
@Getter
@Setter
@NoArgsConstructor
@Slf4j
public class AliYunVodFileStorage implements FileStorage {
    private String platform;
    private String bucketName;
    private String domain;
    private String accessKeyId;
    private String accessKeySecret;
    private String basePath;
    private String defaultAcl;
    private int multipartThreshold;
    private int multipartPartSize;

    public AliYunVodFileStorage(AliYunVodConfig config) {
        platform = config.getPlatform();
        bucketName = config.getBucketName();
        domain = config.getDomain();
        basePath = config.getBasePath();
        defaultAcl = config.getDefaultAcl();
        multipartThreshold = config.getMultipartThreshold();
        multipartPartSize = config.getMultipartPartSize();
        accessKeyId = config.getAccessKey();
        accessKeySecret = config.getSecretKey();
    }

    @Override
    public String getPlatform() {
        return this.platform;
    }

    @Override
    public void setPlatform(String platform) {
        this.platform = platform;
    }

    @Override
    public boolean save(FileInfo fileInfo, UploadPretreatment pre) {
        fileInfo.setBasePath(basePath);
        String newFileKey = getFileKey(fileInfo);
        fileInfo.setUrl(domain + newFileKey);
        ProgressListener listener = pre.getProgressListener();
        CannedAccessControlList fileAcl = getAcl(fileInfo.getFileAcl());
        ObjectMetadata metadata = getObjectMetadata(fileInfo, fileAcl);
        try (InputStreamPlus inputStream = pre.getInputStreamPlus(false)) {
            UploadStreamRequest request = new UploadStreamRequest(this.accessKeyId, accessKeySecret, fileInfo
                    .getFilename(), fileInfo.getOriginalFilename(), inputStream);
            request.setApiRegionId("cn-shenzhen");
            if (listener != null) {
                AtomicLong progressSize = new AtomicLong();
                request.setProgressListener(new VoDProgressListener() {
                    @Override
                    public void onVidReady(String s) {
                    }

                    @Override
                    public void onImageIdReady(String s) {
                    }

                    @Override
                    public void progressChanged(ProgressEvent progressEvent) {
                        if (progressEvent.getEventType() == ProgressEventType.TRANSFER_STARTED_EVENT) {
                            listener.start();
                        } else if (progressEvent.getEventType() == ProgressEventType.REQUEST_BYTE_TRANSFER_EVENT) {
                            listener.progress(progressSize.addAndGet(progressEvent.getBytes()), fileInfo.getSize());
                        } else if (progressEvent.getEventType() == ProgressEventType.TRANSFER_COMPLETED_EVENT) {
                            listener.finish();
                        }
                    }
                });
                /* 视频描述（可选）*/
                request.setPrintProgress(true);
                UploadVideoImpl uploader = new UploadVideoImpl();
                UploadStreamResponse response = uploader.uploadStream(request);
                if (response.isSuccess()) {
                    Dict arrt = fileInfo.getAttr();
                    if (arrt == null) {
                        arrt = new Dict();
                    }
                    arrt.set("videoId", response.getVideoId());
                    fileInfo.setAttr(arrt);
                    fileInfo.setPath("storage/video/star/");
                    fileInfo.setFilename(response.getVideoId() + ".mp4");
                    fileInfo.setUrl(domain + fileInfo.getPath() + fileInfo.getFilename());
                }
                return response.isSuccess();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return true;
    }

    /**
     * 获取文件的访问控制列表
     */
    public CannedAccessControlList getAcl(Object acl) {
        if (acl instanceof CannedAccessControlList) {
            return (CannedAccessControlList) acl;
        } else if (acl instanceof String || acl == null) {
            String sAcl = (String) acl;
            if (StrUtil.isEmpty(sAcl))
                sAcl = defaultAcl;
            for (CannedAccessControlList item : CannedAccessControlList.values()) {
                if (item.toString().equals(sAcl)) {
                    return item;
                }
            }
            return null;
        } else {
            throw ExceptionFactory.unrecognizedAcl(acl, platform);
        }
    }

    /**
     * 获取对象的元数据
     */
    public ObjectMetadata getObjectMetadata(FileInfo fileInfo, CannedAccessControlList fileAcl) {
        ObjectMetadata metadata = new ObjectMetadata();
        if (fileInfo.getSize() != null)
            metadata.setContentLength(fileInfo.getSize());
        metadata.setContentType(fileInfo.getContentType());
        metadata.setObjectAcl(fileAcl);
        metadata.setUserMetadata(fileInfo.getUserMetadata());
        if (CollUtil.isNotEmpty(fileInfo.getMetadata())) {
            fileInfo.getMetadata().forEach(metadata::setHeader);
        }
        return metadata;
    }

    /**
     * 获取缩略图对象的元数据
     */
    public ObjectMetadata getThObjectMetadata(FileInfo fileInfo) {
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(fileInfo.getThSize());
        metadata.setContentType(fileInfo.getThContentType());
        metadata.setObjectAcl(getAcl(fileInfo.getThFileAcl()));
        metadata.setUserMetadata(fileInfo.getThUserMetadata());
        if (CollUtil.isNotEmpty(fileInfo.getThMetadata())) {
            fileInfo.getThMetadata().forEach(metadata::setHeader);
        }
        return metadata;
    }

    @Override
    public boolean delete(FileInfo fileInfo) {
        StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
                .accessKeyId(this.accessKeyId)
                .accessKeySecret(this.accessKeySecret)
                .build());
        AsyncClient client = AsyncClient.builder()
                .region("cn-shenzhen")
                .credentialsProvider(provider)
                .overrideConfiguration(ClientOverrideConfiguration.create()
                        .setEndpointOverride("vod.cn-shenzhen.aliyuncs.com"))
                .build();
        DeleteVideoRequest deleteVideoRequest = DeleteVideoRequest.builder()
                .videoIds(fileInfo.getAttr().getStr("videoId"))
                .build();
        CompletableFuture<DeleteVideoResponse> response = client.deleteVideo(deleteVideoRequest);
        DeleteVideoResponse resp;
        try {
            resp = response.get();
            log.info("视频上传：{}", resp);
        } catch (InterruptedException | ExecutionException e) {
            if (!e.getMessage().contains("The specified parameter \"VideoIds\" is not valid")) {
                throw new RuntimeException(e);
            }
        } finally {
            client.close();
        }
        return false;
    }

    @Override
    public boolean exists(FileInfo fileInfo) {
        return false;
    }

    @Override
    public void download(FileInfo fileInfo, Consumer<InputStream> consumer) {

    }

    @Override
    public void downloadTh(FileInfo fileInfo, Consumer<InputStream> consumer) {

    }
}
